THE ASSEMBLY LANGUAGE "MAGAZINE" VOL 1 NUMBER 3 May, 1989 ## #### #### ####### ## ## ###### #### ## ## #### ## ## ## ## ## # ### ### ## ## ## ## ## ## ## ### ### ## # ####### ## ## ## ## ## ## ## ### ### #### ####### ##### ## #### ###### ### ### ## # ## # ## ## ## ## # ## ## ## ## ## ## ## ## # ## ## ## ## ## ## ## ## ## #### #### ####### ## ## ###### ####### #### #### ## ## ## #### ## ## ## #### ####### ## #### ### ## ## ## ## ## #### ## ## ## # ## ## ## #### ## ## ## ## ## ## ## ## # ## ## ## ## #### ## ## ## ## ## ## #### ## # ###### ## ### ## ### ## ## ###### ## ### ## # ## ## ## ## ## ## ## ## ## ## ## ## ## ## ## # ####### ## ## ## ## ##### ###### ## ## ##### ####### Written by and for assembly language programmers. Table of Contents Table of Contents. . . . . . . . . . . . . . . . . . . . . . 2 Editorial. . . . . . . . . . . . . . . . . . . . . . . . . . 3 GUIDE LINES FOR CONTRIBUTORS . . . . . . . . . . . . . . . . 4 Beginners'Corner . . . . . . . . . . . . . . . . . . . . . . 5 Segmentation . . . . . . . . . . . . . . . . . . . . . . . 5 Keyboard driven TSR programs.. . . . . . . . . . . . . . . . 7 Hex Conversion Routines . . . . . . . . . . . . . . . . . . 12 Book Reviews. . . . . . . . . . . . . . . . . . . . . . . . 14 Source Code for Keyboard TSR. . . . . . . . . . . . . . . . 15 Source for Soft Breakout. . . . . . . . . . . . . . . . . . 18 ;page 2 Editorial This is the third issue of the Magazine, and the first with a major article from an outside contributor. Our thanks to Garrett Nievin. It is amazing how few programmers really appreciate the benefits of assembly language programming. Most of them under- stand that for certain parts of programs it can increase performance, but they have no real conception of the amount of improvement that it can make. As a common example of this-- The case of sprite manipulation. Some of the more speed conscious programmers are aware that to do clean writes to the CGA screen you need to wait for the retrace periods, but a C programmer cannot chase the electron beam around the screen to update it in areas that will not be affected until it again goes over it. This multiplies the time available to manipulate areas of the screen without flicker. Much of the time in executing a higher level routine is spent in calling it and returning from it. The indexed with of- fset stack operations are very costly in time. This is why even speedup assembly routines in high level code don't show the full capabilities as they are still called using the same conventions. One of the most touted buzz words today is "structured". This translates into using small easily controlled and understood subroutines with a single entry point and a single exit point having sharply limited function. This makes for a program that is quick and easy to write (regardless of the language) and quick to debug. These are all valuable attributes in a program, and if done right these same routines can be included in a large variety of dissimilar programs. There are those who say that any programmer who does not keep to "structured" programming is a bad programmer. At least in assembly language programming there are times and reasons for throwing this whole concept away. In doing so you create a program that is almost impossible to modify, totally unreadable, insane to debug, and a nightmare to try and document----BUT----you can also come close to cutting your size (already tiny compared to anything else) in half and have a good chance of a thirty percent increase in speed if you do it right. A program like that is not the product of a "bad" programmer, but of a very dedicated one. In such a program you don't have the time to waste making a lot of CALLS or doing variable storage or register adjustments. The num- ber of items that you must keep track of, mostly in your head, while writing makes it a project for only the best of programmers. I gave it up years ago, but still envy those who are able to make use of a powerful tool. ;page 3 GUIDE LINES FOR CONTRIBUTORS AND 'ADVERTISERS' Name and address must be included with all articles and files. Executable file size and percent of assembly code (when available) should be included when a program is mentioned and is re- quired from an author or publisher. Any article of interest to Assembly language programmers will be considered for inclusion. Quality of writing will not be a factor, but I reserve the right to try and correct spelling errors and minor mistakes in grammar, and to remove sections. Non-exclusive copyright must be given. No monetary compensa- tion will be made. Outlines of projects that might be undertaken jointly are welcome. For example: One person who is capable with hardware needs support from a user friendly programmer and a math whiz. Advertisements as such are not acceptable. Authors and publishers wishing to contribute reviews of their own products will be considered and included as space and time permit. These must include executable file size, percent of assembly code and time comparisons. Your editor would like information on math libraries, and reviews of such. Articles must be submitted in pclone readable format or sent E-mail. Money: Your editor has none. Therefore no compensation can be made for articles included. Subscription fees obviously don't ex- ist. Publication costs I expect to be nil (NUL). Small contributions will be accepted to support the BBS where back issues are available as well as files and programs mentioned in articles(if PD or Shareware ONLY). Shareware-- Many of the programs mentioned in the "Magazine" are Shareware. Most of the readers are prospective authors of programs that can be successfully marketed as Shareware. If you make significant use of these programs the author is entitled to his registration fee or donation. Please help Shareware to continue to be a viable marketing method for all of us by urging everyone to register and by helping to distribute quality programs. ;page 4 Beginners'Corner Segmentation Segments are largely a carry over from the days when processors were all limited to a 16 bit address buss, but we still live with them today. They are not without some benefits however. The way they are treated in most of the books I am familiar with can leave the novice fear struck over the complexities and this is not necessary. They are nothing more ( to the assembly program- mer) than a tool that can either be used or discarded almost at will. The 8088 has 4 segment registers; CS, DS, ES, and SS. The complete address of a memory reference is the sum of the segment register (shifted 1 hex place to the left) and another register. Thus each location in memory can be accessed by many (up to 4096) combinations of segment and offset. For example segment 1000h, offset 1000 (generally written as 1000:1000) can also be addressed as 1001:0ff0, as the sum of both of these add up to 11000h. Each of the segment registers has another register that is hardware as- sociated with it but that can sometimes be overridden in the in- struction. The IP (instruction pointer) is firmly attached to the CS segment. The SP (stack pointer) always points to a location in the SS (stack segment). DI refers mainly to a location in ES as SI does to DS. In standard programming parlance there is the code segment, the stack segment, and the data segment written in various ways such as code_seg. High level language compilers insist on this division and MASM forces you to at least define them, but you should keep in mind that they are only conveniences and should be discarded when they cease to be convenient. In the following discussion I want to make a distinction between two types of data. The first is data that the program contains or uses as intermediate storage. This would include variables addressed in the code, messages to the user, and data buffers that operated on. The second type is external to the program. An example of this is a text file that your program will edit. Let me call the first type program data and the second type external data. The dividing line is very loose and often ceases to exist but it is a convenient fiction for now. The one unchangeable fact that we have to deal with is the way DOS loads the programs. There are two formats it can follow: the .COM format and the .EXE format. The COM format loads quickly because it is simply an image of the machine code and needs no processing by DOS in order to execute. It is limited in size to 64k, but I have never seen an assembly language program that was that large so it is really not much of a limit. All segment registers are set to the same value on loading (this value is the address of the PSP) and the IP is set at 100h. The EXE format is not limited to size but it requires post processing by DOS in order to work. This makes it slower to begin execution. This is also the format that is compatible with the source level debuggers. During linking the linker assigns segment offset values to each of the segments you define in your code. After loading DOS adds this offset value to each location in your ;page 5 code that addresses a segment. This information is contained in a relocation table that the linker prepends to the file. Then DOS sets the CS register to the start of your code segment and the SS register to the start of your stack segment, and DS and ES to the start of the PSP. The IP is set to the offset specified in the END statement as the start of execution. Using either format you are still left with a mess to attend to. Your program data area is not defined or addressed, your stack is out of control (if .COM), and your program owns the entire address space in the computer from the start of your program to the end of memory. The first responsibility of any program is to clean this up. The methods of doing this are very simple and can be s standard header to all of your programs. I won't go into them now, but will try to cover them next issue. For now let's assume that you have attended to that and your whole program with code, stack and program data occupy a few thousand bytes above the PSP,and you have returned everything else to DOS. This is the ideal starting point for any program. Whatever other memory you need for external data can be requested from DOS and it knows how much is there and unused which your program doesn't. It will return to you the segment address of the block you requested. This then you can subdivide into as many segments as you like. For example: you are keeping a database of 10,000 names. Each name could be considered to be 2 segments long (32 characters) so you could move from name to name by adding 2 to the segment register you are using to address this space. Doing it this way frees you from the 64k limit you would have if instead you used one of the index registers and added 32 to it. This is a complex subject to try and explain, especially in a short column. More techniques will be discussed next issue. ;page 6 A fool rambles about Keyboard driven TSR programs. by Garrett P. Nievin 4518 Valley Brook Dr. San Antonio, TX 78238 o Introduction First of all, let me describe exactly how a keyboard driven TSR SHOULD work, to be a friendly inhabitant of your system. To me, there are two kinds of such programs: those which modify keyboard functioning (as in the case of Superkey), and those which merely check for keystrokes to activate a resident function (such as Sidekick). I will focus my discussion on the latter, but the basic principles apply to any keyboard TSR. o Narrative of how a friendly TSR handles the keyboard. To reliably and safely monitor the keyboard for a keypress, a program must install an interrupt handler for interrupt 9, which is hardware generated every time a key is pressed or released. For now let's assume we have a routine in memory and is the active interrupt 9 handler. Every time a key is pressed or released, our program automagically wakes up. The only affected registers are CS:IP, which point to where our program is executing of course. Our program should IN a byte from the keyboard port; this is the scancode which we have been summoned to service. Since the TSR program is only checking for a certain keyboard condition (such as an Alt-Q being hit), all other conditions should be ignored. If it is not "our" key, then we want to do nothing more. To relin- quish control, we do a long JMP to the old interrupt 9 handler (which may be BIOS ROM, or it may be another TSR. In this friend- ly manner, any number of TSR's may happily coexist). If it IS our key, we perform whatever action is appropriate, and terminate by one of two methods: jump to the old int handler, which allows the rest of the TSR's and/or BIOS to get at the same keypress; or, terminate the interrupt ourselves. This second method involves 3 basic steps: 1) Tell the keyboard we have serviced his keypress 2) Tell the hardware interrupt controller we have serviced the hardware interrupt to completion and 3) perform and IRET (return from interrupt) instruction to continue with system processing. o How to read the keyboard The keyboard is driven by an 8048 chip, which is tied logically to the 8259A Peripheral Interrupt Controller (as level 1; only the timer interrupt at level 0 has more priority) and to the port A and port B of the 8255A-5 Programmable Peripheral Interface chip. I only give you these numbers to impress people with; that, and to get a little better understanding of what all happens. The scan code of a keypress gets placed in port A of the 8255, which is mapped to I/O port 60h of the CPU. All keypress scancodes will be in the range 01 (Escape) to 53h (Delete). If the high bit is set on, then it is not a keypress scancode, but a key release scan- code. These are usually ignored, but must be processed non- etheless. ;page 7 Port A can be safely IN'd from any number of times before the keyboard is cleared; this is done by getting port B (IN AL,61h), set the hi bit on (OR AL,80h) and outing it back to port B. Once the keyboard is cleared, you usually want to tell the 8259 PIC that the interrupt you've been servicing has been completed by outing 20h to port 20h. The 8259 prioritizes all interrupts, and this command tells it that the highest priority interrupt is finished; this is neat because all the interrupt handlers don't have to know what level interrupt they are. Now comes the obvious question: How are we going to check for 2 keys, as in the case of say Alt Q? BIOS makes this one easy. In the BIOS/DOS data area (segment 40), at bytes 17h and 18h, we find information on which special keys are being pressed. The sig- nificant bits are: 17h xxxxXXXX 18h XXXXxxxx 1 Alt is being pressed 1 Insert is being pressed 1 Ctrl 1 Caps Lock 1 Left shift 1 Num Lock 1 Right shift 1 Scroll Lock All we do is wait for the scancode of Q to come up. Then we check the appropriate bit in the appropriate flag byte (in the case of alt, we would test 0040:0017h for 08h). If the bit is on, we do our thing. Otherwise we just pass control on to the next in- terrupt handler in the string with our long JMP. NOTE: always use a "normal" key as a "wakeup call"; that is, don't check for combos like ALT-Rshift. There are too many programs already that do this, and programs employing these keys are difficult to chan- ge, thereby ensuring incompatibilities. In fact, if you dis- tribute your program and don't offer some means of "hot key" con- figuration, you may want to tell your users where your scan codes are located in the program so they may patch them themselves to avoid incompatibilities. Hint: use a CMP and not a TEST to check this byte, or if you are looking for ctrl-A then ctrl-alt-A, ctrl-shift-A, etc. will all mistakenly trigger your interrupt. o How to install the TSR The installation program will consist of 3 parts really: the jump around the interrupt handler code to the install code, and the in- terrupt and install code areas. The install code should do basi- cally two things: install the interrupt handler using service 25h of interrupt 21h (DOS call), and of course terminate and-stay-resident. To do the install, load AX with 2509h (AL has the interrupt number) and DS:DX with the address of the interrupt handler code, and do an int 21h. Your program is RIGHT NOW the new interrupt 9 handler. Then, load AX with 3100h and do an int 21h again (Terminate and stay res and return an err code of 00 to parent). At this point the handler is working, in memory, and control is released back to the parent program (in most cases Command.com). Here is our opportunity to start getting fancy. To keep memory usage to a minimum, we should do a DOS service 4Ah to release all unneeded memory. ;page 8 /* UPDATE: I don't know what I was thinking here, but that's wrong. You don't use service 4Ah in a TSR; it would do nothing. What you DO do is load the number of paragraphs you need to keep into the DX register when you do your service 31h call. The way I get that is to use: ((last_address-first_address)/16)+1 (to div by 16 just shift right 4 bits) And don't forget to allow room for your PSP! */ To REALLY cut down on the memory, we can relocate our program backwards into the unused PSP area, saving something like 164 bytes. Also, we can leave a memory signature so that the user is not allowed to install the TSR more than once. My usual method of this is pretty simplistic. I put 4 unique bytes into the TSR it- self, and then upon installation check if those bytes are there. If so, the installation aborts and the user is told. The flaw here is that it only prevents the same program being installed after itself. If such a TSR is loaded and takes int 9, another one is later loaded and takes int 9 also, the program could then be installed for the second time. I know how my system is con- figured, so this is no problem. To a novice user getting hold of one of my programs, this will at best just waste a little memory. At worst, the system can get a little weird. To more securely guard against re-installation, you may take another interrupt vec- tor (there are 256, after all, and only a couple dozen are used in a basic PC). The way that works is: search for an empty in- terrupt vector (one which has all zeros), and take one when you find it. Save a memory signature there. When the program in- stalls the TSR code, search the interrupt vectors for your memory signature. If you find it, you know your program is there al- ready. Much safer. (Aside: For my personal use, I combine all my TSR's into one program. This saves memory, time, and trouble. It also keeps me from installing TSR's in between one another!) o What you can get away with in a TSR If you have read this far, I assume you don't already know all this. So, I will not go into all the undocumented MS-DOS services for TSR writers. If you are interested in them, I suggest you check back issues of magazines like Programmer's Journal and Dr. Dobb's. Anyway, you are restricted as to what can happen inside a TSR. It all boils down to one thing: MS-DOS is not reentrant. It is not meant to be multitasking (under programs like DoubleDos, there are copies of DOS for each task running), and so a routine can not be called while it is already executing for somebody else. Here is a scenario: You are saving a document from your word processor, and during the wait call up a TSR to find a phone num- ber or something. The TSR does a disk read of your phone # file. The TSR finishes and exits back to the word processor. Now, the WP's disk write has been interrupted, losing vital information like where on the disk he was writing, etc. MS-DOS goes awry. It is time for the big red switch, and kiss your document adios. Hope your disk is not messed up as well. There is the why of all this, now here is the what. A TSR may not use any DOS int 21h services higher than 0Ch. It is restricted to console and printer I/O services. (Now, this is of course not TOTALLY true, but how to do disk I/O etc. are beyond the scope of this little rambling. Maybe some other time.) ;page 9 /* UPDATE Okay, here's how to do anything you please from within a TSR, including disk I/O. The BEST way is to install interrupt "front-end" handlers for int 13h (BIOS disk I/O), int 21h (DOS), int 25h (absolute disk read), int 26h (absolute disk write), and I think that's all. In these front-end hand- lers, set a flag (or semaphore or TS byte or whatever you want to call it) whenever anyone is in the middle of a disk I/O. Then, in your TSR, don't do a disk I/O if any of these flags are set. You will also need to steal the user timer tick in- terrupt (1Ch) to periodically check for when those interrupts are free for use. Or, you can use the cheater way (which is used by DOS itself): Whenever DOS is waiting for a keypress, it calls int 28h (undocumented). The only program I know of "legally" using this is the PRINT command. Whenever int 28h is called, you should be free to do whatever you want. If you want more immediate access to the disk, though, you'll need to use the timer int (1Ch) again. ONCE, at initialization time, do an int 21h ah=34h. In ES:BX, you will have the address of a byte called INDOS by most folks. This is an undocumented function which has been officially declared off-limits by Microsoft, but nobody pays attention to them anyway. As far as I know, it works fine in all versions of MS-DOS as of today (April 1988). In your timer interrupt servicer, read the byte at the address you got from service 34h. If it's not zero, don't do anything over service 0ch; if it's zero, go crazy. What this byte is (I'm told) is a count of how many calls deep DOS is into itself. The actual value is of no relevance to us, just if it is zero or not. Now, I don't see this helping much if the program is doing BIOS I/O, but I don't use any software I know of which uses BIOS for disk I/O. Also, if the application is doing direct-to-the-hardware disk I/O, I don't think you probably want to be messing with this stuff. Again, I don't use anything that I know of doing this. I've written several programs using the int 28h/service 34h combo to do TSR disk I/O and have never had a single problem, and I tested them in the middle of all kinds of disk accesses. This does not mean it's always cool, though. If you can help it, al- ways avoid undocumented stuff. In my opinion, Microsoft won't change it because so much software relies on it now, but I've learned not to trust them. o Example program complete with flimsy excuses The Sperry PC has a high-res graphics screen which is treated as a logically different display from the normal text screen, and uses different display memory banks. The text bank, called screen A, can be superimposed over the graphics (called screen B) on the screen simultaneously. This program switches between screen A on- ly, screen B only, and screen A superimposed on screen B. This program is for example only, and you should not attempt to run it on a CGA. If something gets fried, it's not my fault. It looks for a keypress of Alt-Esc, goes into action, and then quits. I hope this has been helped you understand how a basic keyboard driven TSR works. If you have any more questions, feel free to contact me with them, and I will find you an answer. Most ad- vanced TSR functions (almost all are undocumented by Microsoft and ;page 10 IBM!) have been discussed in Byte, PC Tech Journal, and Dr. Dobb's Journal, but have been thoroughly discussed in Programmer's Journal. I highly recommend this publication, and hope more people will subscribe, so it does not fold up and deprive me of all the information it provides. Call the Telstar BBS: (512)822-8882 for a good time Garrett P. Nievin has also contributed a piece of code to service a soft breakout switch for Symdeb and Debug. Please see the listing in the source code section. Both of these are extremely well commented and should be quite useful. ;page 11 Hex Conversion Routines Just a little "quicky" to fill out this issue. These are a couple of routines that I use regularly in a variety of ways. They are both fast and fairly tightly coded and they work. I hope you find them of some use. The first, DECHEX, takes an ASCII number pointed to by SI and returns a hex number in AX. The other just reverses the process, and when used with the little MOV_ASCII puts it where you tell it to. ;~ DECHEX: ;THIS ROUTINE WILL TAKE A [CL] (MAX 5) DIGIT ;ASCII DECIMAL NUMBER POINTED TO BY SI AND ;RETURN A 4 DIGIT HEX NUMBER IN AX.* XOR DH,DH XOR AX,AX TRY: CMP CL,1 JBE LASTA MOV DL,[SI] SUB DL,"0" ADD AX,DX MOV BX,AX SHL AX,1 SHL AX,1 SHL AX,1 ADD AX,BX ADD AX,BX INC SI LOOP TRY LASTA: MOV DL,[SI] SUB DX,"0" ADD AX,DX RET HEXDEC: ;THIS ROUTINE WILL TAKE A 4DIGIT HEXADECIMAL NUMBER IN AX AND ;RETURNS A 5 DIGIT ASCII IN BH,BL,DH,DL,AL. ;TIME AVERAGES ABOUT 140 CLOCKS PER DIGIT ;LEADING 0'S ARE SUPRESSED MOV BX,0 MOV CX,0 MOV DX,0 A10K: CMP AX,10000 JB A1K SUB AX,10000 INC BH JMP A10K A1K: CMP AX,1000 JB HUNDREDS SUB AX,1000 INC BL JMP A1K HUNDREDS: CMP AX,100 JB TENS SUB AX,100 INC DH JMP HUNDREDS ;page 12 TENS: CMP AX,10 JB UNITS SUB AX,10 INC DL JMP TENS UNITS: MOV AH,'0' ADD BH,AH ADD BL,AH ADD DH,AH ADD DL,AH ADD AL,AH CMP BH,AH JNZ UEND MOV BH,020H U1: CMP BL,AH JNZ UEND CMP BH,020H JNZ UEND MOV BL,020H CMP DH,AH JNZ UEND CMP BL,020 JNZ UEND MOV DH,020H CMP DL,AH JNZ UEND CMP DH,020 JNZ UEND MOV DL,020H UEND: RET MOV_ASCII: ;THIS TAKES THE DIGITS PRODUCED BY ;HEXDEC AND PUTS THEM AT DS:SI MOV [SI],BH INC SI MOV [SI],BL INC SI MOV [SI],DH INC SI MOV [SI],DL INC SI MOV [SI],AL RET ;~ ;page 13 Book Reviews The Programmers' Reference I made a quick review of this last month, but I have since gone over it thoroughly and received the extended package from the author. This is NOT an instruction book for beginners. It is just what it names itself- a reference manual. The shareware version is 10 chapters (about 600k bytes) of solid information and a table of contents. Much of the information is very difficult to find elsewhere. The first chapter is a quick history of DOS from 1.0 to 4.0. Chapter 2 has the Port addresses and the Interrupts up to 0fh. Chapter 3 has the true Bios interrupts 10h to 1fh and it throws in INT 20h for some strange reason. This isn't just a listing that you might find in many places, but a detailed description with calling conventions and special things to watch out for. It includes semi-documented functions such as all of the Desqview and Topview int patches that go into this area. Each interrupt lists what machines it is included in from Tandy to PCjr to PS-2 model 80. I haven't counted them, but there must be thousands. The DOS interrupts 20h through 4fh are covered in similar detail, with many notes and application information. Further chapters contain information on file structures, EMS memory, and many other topics. The registered version gives another disk full of miscellaneous data that is rare and useful. The $15 price is one of the greatest bargains to be found. Available on many BBS's as ####ref.??? such as 1029ref.zip. This says that it is the October 29th release, and the extension is up to the Sysop. ;page 14 Source Code for Keyboard TSR ; High Res screen A/B toggler for Sperry PC, the best PC around ; Written for MASM 4.0 code segment para public 'code' assume cs:code,ds:code,es:code org 0100h ; .COM format start proc far ; Equates - ports, locations, values ; Using equates makes the program much easier to change later; ; to use a different scancode here, for example, you would just ; change the equate for "esc". if1 ; only assemble equates on pass 1 portB equ 61h ; 8259 port B - used to clear scancode keyport equ 60h ; 8259 port A - keyboard scan codes esc equ 01h ; scan code for Escape key hibiton equ 80h ; hi bit on for keybreak scan code & reset hibitof equ 7fh ; hi bit off for keybd reset DOS_area equ 40h ; segment of BIOS/DOS data area shftstat equ 17h ; keybd shft status flag in DOS area shift equ 00000011b ; value for shftstat meaning either shift is on eoi equ 20h ; end of interrupt flag comdport equ 20h ; 8259 command port endif begcode: jmp implant ; jump around interrupt handler code begres: ; area to remain core resident rescode proc ; keystroke causes jump to resident code here jmp oversign ; jump over memory signature db 'abGN' ; program signature in memory ; I use my initials in uppercase and a 2-byte program ID in lowercase ; Variables mode db 0 ; superimpose mode: 0-A,1-B,2-Both,3-None oldint9 dw 0,0 ; doubleword value of old int9 vector oversign: ; start of code for new interrupt 09h handler push ax ; save that register push ds ; save DS. mov ax,DOS_area ; DOS data segment mov ds,ax ; now covered by DS test byte ptr ds:[shftstat],shift ; is a shift being held now? jz normal ; nope, not a hot key, pass it on to old int in al,keyport ; yes, get keyboard scan code cmp al,esc+hibiton ; was it release of escape? je abnorm ; yes, lose that scan code cmp al,esc ; was it an escape? jne normal ; nope, skip the important stuff ; mov al,mode ; get mode cmp al,2h ; both screens on? ;page 15 je aonly ; yes, go back to first add al,1h ; add 1 to hi nybble to go B to A to AB jmp nextmode ; and go put it back aonly: ; start back with screen A only xor al,al ; turn off nextmode: ; put in new mode and quit mov mode,al ; move in new mode mov ah,15h ; set superimpose mode function of int 10h int 10h ; and go change mode jmp abnorm ; lose scan code ; abnorm: ; abnormal handling of scan code. to the bit bucket with it. in al,portB ; get portB or al,hibiton ; set acknowledge / clear keyboard bit out portB,al ; and out it again and al,hibitof ; clear ack bit out portB,al ; out that, enabling keyboard again cli ; no interruptions please, Mrs. a-Whiggins mov al,eoi ; end-of-interrupt command out comdport,al ; send it to the 8259 pop ds ; restore pop ax ; registers sti ; interruptable again iret ; quit without performing normal interrupt normal: ; not hot key, pass scan code on to normal (maybe) int9 pop ds ; restore all pop ax ; registers I messed with jmp dword ptr cs:oldint9 ; goto old int9 handler ; all code from here out is based on Norton's guide, and I use it in ; all my TSR's. reslen equ endres - begres ; length of new resident code strtres equ begres - begcode + 100h ; start of resident code psplen equ 5ch ; length of necessary PSP implant: ; code to put new int 9 front end in core mov ax,3509h ; get int vector function int 21h ; get vector of interrupt 9 handler cmp es:[bx+3],'ba' ; am I already loaded? jne fresh ; nope, go install cmp es:[bx+5],'NG' ; make sure I'm not already in jne fresh ; naaah, go install lea dx,cs:stalemsg ; DX points to already installed message mov ah,09h ; DOS display string func int 21h ; go display message int 20h ; and quit fresh: lea dx,cs:instlmsg ; DX points to installation message mov ah,09h ; display string function int 21h ; give the user the poop mov ax,3509h ; get int vector function int 21h ; get vector of old interrupt 9 handler mov oldint9,bx ; int location in ES:BX, save it ;page 16 mov oldint9+2,es ; " mov ax,2509h ; set new interrupt 9 vector to me mov dx,offset rescode ; address of which is in DX int 21h ; go do it push cs ; DS and ES both end pop es ; up pointing at push cs ; code area pop ds ; " mov di,psplen ; where program will end up mov si,strtres ; start of resident code mov cx,reslen ; amount of resident code to move cld ; go forward in move rep movsb ; move code back in mem mov dx,psplen + reslen ; DX pointing to next free paragraph mov ax,3100h ; keep, return code of 0 int 21h ; terminate stalemsg db 'Screen A/B toggle is already installed and active!',13,10,7,7 instlmsg db 'High Res Screen A/B toggle',13,10 db 'Shift/Escape toggles screen A only, then B only, then A/B.' db 13,10,'$' start endp code ends end start ;page 17 Source for Soft Breakout ; BUGOUT.COM ; Program to add wimpy "soft" breakout key to DEBUG and SYMDEB ; ; MASM compatible source code ; ; Hitting the '5' on keypad forces an int 3 (debug breakpoint) ; After breakpoint, add 1 to IP register and trace through IRET ; ; By Garrett P. Nievin ; ; (BTW, to add a hardware breakout switch just put a switch between ; pins 17 (NMI) and 40 (+5V) on your 8088 chip, and a switch between ; pins 21 (RESET) and 40 will do a system reset/reboot.) code segment para public 'code' assume cs:code,ds:nothing,es:nothing org 0100h hotkey equ 4ch ; scan code for hot key = "5" on keypad portA equ 60h ; 8255A port A, usually keybd scan code portB equ 61h ; 8255A port B, various switches release equ 80h ; hi bit in scan code means key release hibiton equ 80h ; hi bit in byte for or'ing in hbitoff equ 7fh ; hi bit in byte for and'ing out start proc far begcode: jmp implant ; jump around interrupt handler code begres: ; area to remain core resident rescode proc ; keystroke causes jump to here jmp over ; jump over memory signature db 'bcGN' ; program signature in memory over: ; sti ; allow interrupts push ax ; save that register in al,portA ; get keyboard scan code cmp al,hotkey ; hotkey? jne normal ; yes, go hanlit push dx ; save register mov dx,0020h ; port 20h = PIC port to send EOI to mov al,dl ; 20h turns on EOI bit out dx,al ; tell interrupt controller we be done pop dx ; restore register in al,portB ; get portB or al,hibiton ; set acknowledge / clear keyboard bit out portB,al ; and out it again and al,hbitoff ; clear ack bit out portB,al ; out that, enabling keyboard again pop ax ; restore original register int 3 ; iret ; ;page 18 normal: ; pop ax ; restore that register jmp dword ptr cs:oldint9 ; goto old int9 handler oldint9 dw 0,0 ; doubleword value of old int9 vector rescode endp endres: ; end of core res area reslen equ endres - begres ; length of new resident code strtres equ begres - begcode + 100h ; start of resident code psplen equ 5ch ; length of necessary PSP implant: ; code to put new int 9 front end in core mov ax,3509h ; get int vector function int 21h ; get vector of interrupt 9 handler cmp es:[bx+3],'cb' ; already loaded? jne fresh ; nope, go install cmp es:[bx+5],'NG' ; make sure jne fresh ; naaah, go install lea dx,cs:stalemsg ; DX points to already installed message mov ah,09h ; DOS display string func int 21h ; go display message mov ax,4cffh ; terminate with code = 0xff int 21h ; fresh: lea dx,cs:instlmsg ; DX points to installation message mov ah,09h ; display string function int 21h ; give the user the poop mov ax,3509h ; get int vector function int 21h ; get vector of old interrupt 9 handler mov oldint9,bx ; int location in ES:BX, save it mov oldint9+2,es ; " mov ax,2509h ; set new interrupt 9 vector to me mov dx,offset rescode ; address of which is in DX int 21h ; go do it push cs ; DS and ES both end pop es ; up pointing at push cs ; code area pop ds ; " mov di,psplen ; where program will end up mov si,strtres ; start of resident code mov cx,reslen ; amount of resident code to move cld ; go forward in move rep movsb ; move code back in mem mov dx,psplen + reslen ; DX pointing to next free paragraph mov ax,3100h ; keep, return code of 0 int 21h ; terminate stalemsg db 'Bugout already resident.',10,13,7,'$' instlmsg db 'Bugout loaded and active, keypad-5 is break key.',10,13,'$' start endp code ends end start ;page 19